package com.flex.reflection.invoke;

import com.flex.reflection.MetaMethod;
import com.flex.reflection.MethodWrapper;
import com.flex.reflection.handler.Handler;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author ganlt
 * @since 2019/09/20 13:12
 */
public class InvokeMethod implements Invoke, MethodInvoke {
    private MetaMethod methodMeta;
    private List<Handler> handlers = new ArrayList<>();

    public InvokeMethod(MetaMethod method) {
        this.methodMeta = method;
    }

    public InvokeMethod(MetaMethod method, Handler handler) {
        this.methodMeta = method;
        this.handlers.add(handler);
    }


    @Override
    public Object invoke(Object target, Object[] args) {

        Method method = this.methodMeta.getMethod();

        MethodWrapper wrapper = new MethodWrapper();
        wrapper.setArgs(new ArrayList<>(Arrays.asList(args)));
        wrapper.setInstance(target);
        wrapper.setMethod(method);
        for (Handler handler : handlers) {
            handler.beforeHandler(methodMeta, wrapper);
        }
        Object invoke = methodMeta.invoke(args);
        wrapper.setResult(invoke);
        for (Handler handler : handlers) {
            Object resResult = handler.afterHandler(methodMeta, wrapper);
            if (resResult != null) {
                return resResult;
            }
        }
        return invoke;
    }

    @Override
    public List<Type> parameterType() {
        return methodMeta.paramsType();
    }

    @Override
    public Type returnType() {
        return methodMeta.returnType();
    }

    @Override
    public List<Handler> getHandlers() {
        return Collections.unmodifiableList(this.handlers);
    }

    @Override
    public void clearHandler() {
        handlers.clear();
    }

    @Override
    public void addHandler(Handler handler) {
        this.handlers.add(handler);
    }
}
